﻿#include "CppCodeGenerator.h"
#include "XyNetCompiler/ServiceParser.h"

namespace XyNet
{

	void CppCodeGenerator::generator(ServiceParser* parser)
	{
		auto serviceFilePath = parser->getServiceFilePath();
		auto basePath = serviceFilePath.parent_path() / "cpp";
		filesystem::create_directories(basePath);
		basePath /= serviceFilePath.stem();
		filesystem::path headerPath = basePath.string() + ".service.h";
		filesystem::path sourcePath = basePath.string() + ".service.cpp";

		generatorHeaderFile(parser, headerPath);
		generatorSourceFile(parser, sourcePath);
	}

	void CppCodeGenerator::generatorHeaderFile(ServiceParser* parser, const filesystem::path& headerPath)
	{
		filesystem::ofstream headerStream(headerPath);

		// 写入必须的头文件
		headerStream << "#pragma once" "\n" <<
			R"(#include "XyNet/Service/RpcArgument.h")" "\n"
			R"(#include "XyNet/Service/RpcCallback.h")" "\n"
			R"(#include "XyNet/Channel/RpcChannel.h")" "\n"
			R"(#include "XyNet/Service/RpcController.h")" "\n"
			R"(#include "XyNet/Service/RpcMethod.h")" "\n"
			R"(#include "XyNet/Service/RpcService.h")" "\n" << boost::format(
				R"(#include "%s.message.pb.h")" "\n\n") % headerPath.stem().string();

		// 写入package
		auto package = parser->getPackage();
		if (!package.empty())
			headerStream << boost::format("namespace %s" "\n") % package << "{" "\n\n";

		// 写入错误定义
		if (!parser->getErrors()->empty())
		{
			auto errorsName = headerPath.stem().stem().string() + "Errors";
			headerStream <<
				"class " + errorsName + "\n" <<
				"{" "\n"
				"public:" "\n"
				"\t" "static shared_ptr<RpcError> createError(int code);" "\n\n";

			for (const auto& pair : *parser->getErrors())
			{
				const auto& error = pair.second;
				headerStream << boost::format(
					"\t" "static const int %s = 0x%s;" "\n") % error._name % error._codeString;
			}

			headerStream <<
				"};" "\n\n";
		}

		// 写入服务
		auto services = parser->getServices();
		for (const auto& service : *services)
		{
			headerStream << boost::format(
				"class %s : public XyNet::RpcService" "\n") % service._name <<
				"{" "\n"
				"public:" "\n";

			// 头文件写入方法
			for (const auto& method : service._methods)
			{
				headerStream << boost::format(
					"\t" "class %sMethod : public XyNet::RpcMethod" "\n") % method._name <<
					"\t" "{" "\n"
					"\t" "public:" "\n";

				// 请求应答
				headerStream <<
					boost::format("\t\t" "typedef %sMessage::%sRequest Request;" "\n") %
					service._name % method._name <<
					boost::format("\t\t" "typedef %sMessage::%sResponse Response;" "\n\n") %
					service._name % method._name;

				// 控制器
				if (method._errors.empty())
					headerStream << "\t\t" "typedef XyNet::RpcController Controller;" "\n\n";
				else
				{
					headerStream <<
						"\t\t" "class Controller : public XyNet::RpcController" "\n"
						"\t\t" "{" "\n"
						"\t\t" "public:" "\n"
						"\t\t\t" "Controller(XyNet::RpcSocketChannel* socketChannel);" "\n\n"
						"\t\t\t" "virtual shared_ptr<XyNet::RpcError> createError(int code) const override;" "\n\n"
						"\t\t" "public:" "\n";
					for (const auto& error : method._errors)
					{
						headerStream << boost::format(
							"\t\t\t" "static const int %s = 0x%s;" "\n") %
							error._name % error._codeString;
					}
					headerStream << "\t\t" "};" "\n\n";
				}

				// 参数
				headerStream <<
					"\t\t" "class Argument : public XyNet::RpcArgument" "\n"
					"\t\t" "{" "\n"
					"\t\t" "public:" "\n"
					"\t\t\t" "Argument();""\n\n"
					"\t\t\t" "shared_ptr<Controller> getController();" "\n"
					"\t\t\t" "shared_ptr<const Request> getRequest() const;" "\n"
					"\t\t\t" "shared_ptr<Response> getResponse();" "\n\n"
					"\t\t" "private:" "\n"
					"\t\t\t" "virtual shared_ptr<XyNet::RpcArgument> clone() override;" "\n"
					"\t\t\t" "virtual void initializeController(XyNet::RpcSocketChannel* socketChannel) override;" "\n"
					"\t\t" "};" "\n\n";

				// 回调
				headerStream <<
					"\t\t" "class Callback : public XyNet::RpcCallback" "\n"
					"\t\t" "{" "\n"
					"\t\t" "public:" "\n"
					"\t\t\t" "Callback(shared_ptr<XyNet::RpcArgument> argument," "\n"
					"\t\t\t\t" "const function<void(Argument*)>& responseHandler," "\n"
					"\t\t\t\t" "const function<void(Argument*)>& exceptionHandler);" "\n\n"
					"\t\t" "private:" "\n"
					"\t\t\t" "virtual shared_ptr<XyNet::RpcCallback> clone() override;" "\n"
					"\t\t\t" "virtual void doResponseHandler() override;" "\n"
					"\t\t\t" "virtual void doExceptionHandler() override;" "\n\n"
					"\t\t" "private:" "\n"
					"\t\t\t" "function<void(Argument*)> _responseHandler;" "\n"
					"\t\t\t" "function<void(Argument*)> _exceptionHandler;" "\n"
					"\t\t" "};" "\n\n";

				// Method
				headerStream << boost::format(
					"\t\t" "%sMethod(shared_ptr<XyNet::RpcChannel> channel);" "\n\n") % method._name <<
					"\t\t" "shared_ptr<Request> getRequest();" "\n"
					"\t\t" "void call(" "\n"
					"\t\t\t" "const function<void(Argument*)>& responseHandler =" "\n"
					"\t\t\t" "function<void(Argument*)>()," "\n"
					"\t\t\t" "const function<void(Argument*)>& exceptionHandler =" "\n"
					"\t\t\t" "function<void(Argument*)>());" "\n\n"
					"\t" "private:" "\n"
					"\t\t" "shared_ptr<XyNet::RpcChannel> _channel;" "\n"
					"\t\t" "shared_ptr<Argument> _argument;" "\n"
					"\t" "};" "\n\n";

				// 服务方法
				headerStream << boost::format(
					"\t" "virtual void on%s(%sMethod::Argument* argument) = 0;" "\n\n") % method._name % method._name;
			}

			// 头文件写入服务方法函数
			headerStream << boost::format(
				"\t" "%s();" "\n\n") % service._name <<
				"\t" "virtual shared_ptr<XyNet::RpcArgument> createMethodArgument(int methodId) const override;" "\n"
				"\t" "virtual void onMethod(int methodId, XyNet::RpcArgument* argument) override;" "\n"
				"};" "\n\n";
		}

		if (!package.empty())
			headerStream << "}" "\n";
	}

	void CppCodeGenerator::generatorSourceFile(ServiceParser* parser, const filesystem::path& sourcePath)
	{
		filesystem::ofstream sourceStream(sourcePath);

		// 写入必须的头文件，utf8 bom
		sourceStream << char(0xEF) << char(0xBB) << char(0xBF) << boost::format(
			R"(#include "%s.h")" "\n") % sourcePath.stem().string() <<
			R"(#include "XyNet/Common/Cast.h")" "\n"
			R"(#include "XyNet/Common/Exception.h")" "\n\n"
			"using namespace XyNet;" "\n\n";

		// 写入package
		auto package = parser->getPackage();
		if (!package.empty())
			sourceStream << boost::format("namespace %s" "\n") % package << "{" "\n\n";

		// 写入错误定义
		if (!parser->getErrors()->empty())
		{
			auto errorsName = sourcePath.stem().stem().string() + "Errors";
			sourceStream << boost::format(
				"shared_ptr<RpcError> %s::createError(int code)" "\n") % errorsName <<
				"{" "\n"
				"\t" "static const map<int, RpcError> _errors = {" "\n";

			for (const auto& pair : *parser->getErrors())
			{
				const auto& error = pair.second;
				sourceStream << boost::format("\t\t" R"({ %s, { %s, "%s", "%s" } },)" "\n") %
					error._name % error._name % error._name % error._brief;
			}

			sourceStream <<
				"\t" "};" "\n\n"
				"\t" "auto iter = _errors.find(code);" "\n"
				"\t" "if (iter != _errors.end())" "\n"
				"\t\t" "return make_shared<RpcError>(iter->second);" "\n"
				"\t" "return make_shared<RpcError>(_errors.at(RpcErrors::UnknownError));" "\n"
				"}" "\n\n";
		}

		// 写入服务
		auto services = parser->getServices();
		for (const auto& service : *services)
		{
			// 源文件参数和回调方法
			for (const auto& method : service._methods)
			{
				string serviceMethodName =
					(boost::format("%s::%sMethod") % service._name % method._name).str();

				// controller
				if (!method._errors.empty())
				{
					sourceStream << boost::format(
						"%s::Controller::Controller(RpcSocketChannel* socketChannel)" "\n") % serviceMethodName <<
						"\t" ": RpcController(socketChannel) {}" "\n\n" << boost::format(
							"shared_ptr<RpcError> %s::Controller::createError(int code) const" "\n") % serviceMethodName <<
						"{" "\n"
						"\t" "switch (code)" "\n"
						"\t" "{" "\n";
					for (const auto& error : method._errors)
					{
						sourceStream << boost::format(
							"\t" "case (int)0x%s:" "\n") % error._codeString;
						sourceStream << boost::format(
							"\t\t" R"(return std::make_shared<RpcError>(0x%s, "%s", "%s");)" "\n") %
							error._codeString % error._name % error._brief;
					}
					sourceStream <<
						"\t" "default:" "\n"
						"\t\t" "return RpcController::createError(code);" "\n"
						"\t" "}" "\n"
						"}" "\n\n";
				}

				// argument
				sourceStream << boost::format(
					"%s::Argument::Argument()" "\n") % serviceMethodName <<
					"\t" ": RpcArgument(std::make_shared<Request>(), std::make_shared<Response>()) {}" "\n\n";

				sourceStream << boost::format(
					"shared_ptr<%s::Controller> %s::Argument::getController()" "\n") %
					serviceMethodName % serviceMethodName <<
					"{" "\n"
					"\t" "return XYNET_POINTER_DOWNCAST<Controller>(_controller);" "\n"
					"}" "\n\n";

				sourceStream << boost::format(
					"shared_ptr<const %s::Request> %s::Argument::getRequest() const" "\n") %
					serviceMethodName % serviceMethodName <<
					"{" "\n"
					"\t" "return XYNET_POINTER_DOWNCAST<Request>(_request);" "\n"
					"}" "\n\n";

				sourceStream << boost::format(
					"shared_ptr<%s::Response> %s::Argument::getResponse()" "\n") %
					serviceMethodName % serviceMethodName <<
					"{" "\n"
					"\t" "return XYNET_POINTER_DOWNCAST<Response>(_response);" "\n"
					"}" "\n\n";

				sourceStream << boost::format(
					"shared_ptr<RpcArgument> %s::Argument::clone()" "\n") % serviceMethodName <<
					"{" "\n"
					"\t" "XYNET_CHECK(!_controller);" "\n"
					"\t" "auto copy = std::make_shared<Argument>();" "\n"
					"\t" "copy->_request->CopyFrom(*_request);" "\n"
					"\t" "copy->_response->CopyFrom(*_response);" "\n"
					"\t" "return copy;" "\n"
					"}" "\n\n";

				sourceStream << boost::format(
					"void %s::Argument::initializeController(RpcSocketChannel* socketChannel)" "\n") % serviceMethodName <<
					"{" "\n"
					"\t" "XYNET_CHECK(!_controller);" "\n"
					"\t" "_controller = std::make_shared<Controller>(socketChannel);" "\n"
					"}" "\n\n";

				// callback
				sourceStream << boost::format(
					"%s::Callback::Callback(" "\n") % serviceMethodName <<
					"\t" "shared_ptr<RpcArgument> argument," "\n"
					"\t" "const function<void(Argument*)>& responseHandler," "\n"
					"\t" "const function<void(Argument*)>& exceptionHandler)" "\n"
					"\t" ": RpcCallback(argument)" "\n"
					"\t" ", _responseHandler(responseHandler), _exceptionHandler(exceptionHandler) {}" "\n\n";

				sourceStream << boost::format(
					"shared_ptr<RpcCallback> %s::Callback::clone()" "\n") % serviceMethodName <<
					"{" "\n"
					"\t" "return std::make_shared<Callback>(_argument->clone(), _responseHandler, _exceptionHandler);" "\n"
					"}" "\n\n";

				sourceStream << boost::format(
					"void %s::Callback::doResponseHandler()" "\n") % serviceMethodName <<
					"{" "\n"
					"\t" "if (_responseHandler)" "\n"
					"\t\t" "_responseHandler(XYNET_DOWNCAST<Argument*>(_argument.get()));" "\n"
					"}" "\n\n";

				sourceStream << boost::format(
					"void %s::Callback::doExceptionHandler()" "\n") % serviceMethodName <<
					"{" "\n"
					"\t" "if (_exceptionHandler)" "\n"
					"\t\t" "_exceptionHandler(XYNET_DOWNCAST<Argument*>(_argument.get()));" "\n"
					"\t" "else" "\n"
					"\t\t" "XYNET_HANDLE_EXCEPTION(Exception(_argument->getController()->getError()->getInfo()));" "\n"
					"}" "\n\n";

				// method
				sourceStream << boost::format(
					"%s::%sMethod(shared_ptr<RpcChannel> channel)" "\n") %
					serviceMethodName % method._name;
				sourceStream << boost::format("\t" R"(: RpcMethod("%s", 0x%s, 0x%s), _channel(channel), _argument(std::make_shared<Argument>()) {})" "\n\n") %
					method._name % method._id % service._id;

				sourceStream << boost::format("shared_ptr<%s::Request> %s::getRequest()" "\n") %
					serviceMethodName % serviceMethodName <<
					"{" "\n"
					"\t" "return const_pointer_cast<Request>(" "\n"
					"\t\t" "XYNET_POINTER_DOWNCAST<Argument>(_argument)->getRequest());" "\n"
					"}" "\n\n";

				sourceStream << boost::format(
					"void %s::call(" "\n") % serviceMethodName <<
					"\t" "const function<void(Argument*)>& responseHandler," "\n"
					"\t" "const function<void(Argument*)>& exceptionHandler)" "\n"
					"{" "\n"
					"\t" "_channel->callMethod(this, std::make_shared<Callback>(_argument, responseHandler, exceptionHandler));" "\n"
					"}" "\n\n";
			}

			// 源文件写入服务构造
			sourceStream << boost::format(R"(%s::%s() : RpcService("%s", 0x%s) {})" "\n\n") %
				service._name % service._name % service._name % service._id;

			// 源文件写入createMethodArgument
			sourceStream << boost::format(
				"shared_ptr<RpcArgument> %s::createMethodArgument(int methodId) const" "\n") %
				service._name <<
				"{" "\n"
				"\t" "switch (methodId)" "\n"
				"\t" "{" "\n";

			for (const auto& method : service._methods)
			{
				sourceStream << boost::format(
					"\t" "case (int)0x%s:" "\n") % method._id;
				sourceStream << boost::format(
					"\t\t" "return std::make_shared<%sMethod::Argument>();" "\n") % method._name;
			}

			sourceStream <<
				"\t" "default:" "\n"
				"\t\t" "XYNET_CHECK(false);" "\n"
				"\t\t" "return nullptr;" "\n"
				"\t" "}" "\n"
				"}" "\n\n";

			// 源文件写入callMethod
			sourceStream << boost::format(
				"void %s::onMethod(int methodId, RpcArgument* argument)" "\n") %
				service._name <<
				"{" "\n"
				"\t" "switch (methodId)" "\n"
				"\t" "{" "\n";

			for (const auto& method : service._methods)
			{
				sourceStream << boost::format(
					"\t" "case (int)0x%s:" "\n") % method._id;
				sourceStream << boost::format(
					"\t\t" "on%s(XYNET_DOWNCAST<%sMethod::Argument*>(argument));" "\n") %
					method._name % method._name <<
					"\t\t" "break;" "\n";
			}

			sourceStream <<
				"\t" "default:" "\n"
				"\t\t" "XYNET_CHECK(false);" "\n"
				"\t" "}" "\n"
				"}" "\n\n";
		}

		if (!package.empty())
			sourceStream << "}" "\n";
	}

}